/*
 * Rect.h
 *
 *	Author: stravant
 *	Description:
 *		
 */

#ifndef RECT_H_
#define RECT_H_

#include <iostream>
#include "Vec.h"

namespace priv {
	template <class T>
	inline T min(T a, T b) {
		return (a < b) ? a : b;
	}

	template <class T>
	inline T max(T a, T b) {
		return (a > b) ? a : b;
	}

	template <class T>
	inline T min(T a, T b, T c) {
		b = min(b, c);
		return (a < b) ? a : b;
	}

	template <class T>
	inline T max(T a, T b, T c) {
		b = max(b, c);
		return (a > b) ? a : b;
	}

	template <class T>
	inline T min(T a, T b, T c, T d) {
		b = min(d, b, d);
		return (a < b) ? a : b;
	}

	template <class T>
	inline T max(T a, T b, T c, T d) {
		b = max(b, c, d);
		return (a > b) ? a : b;
	}

	template <class T>
	inline T absDif(T a, T b) {
		T dif = a - b;
		return dif > 0 ? dif : -dif;
	}

	template <class T>
	inline T abs(T n) {
		return (n > 0) ? n : -n;
	}
}

template <class T>
class Rect {
public:
	Rect(): mPosition(), mSize() {}
	Rect(T x, T y, T w, T h): mPosition(x, y), mSize(w, h) {}
	Rect(const Vec<T>& pos, const Vec<T>& size): mPosition(pos), mSize(size) {}
	~Rect() {}

	Rect& operator=(const Rect& r) {
		mPosition = r.mPosition;
		mSize = r.mSize;
		return *this;
	}

	static Rect Between(const Vec<T>& a, const Vec<T>& b) {
		return Rect(
			priv::min(a.x(), b.x()),
			priv::min(a.y(), b.y()),
			priv::abs(a.x() - b.x()),
			priv::abs(a.y() - b.y()));
	}

	static Rect Union(const Rect& a, const Rect& b) {
//		Vec<T> p1 = a.position();
//		Vec<T> p2 = a.position() + a.size();
//		Vec<T> p3 = b.position();
//		Vec<T> p4 = b.position() + b.size();
//		std::cout << "> xs:" << p1.x() << " " << p2.x() << " " << p3.x() << " " << p4.x() << "\n";
//		std::cout << "> ys:" << p1.y() << " " << p2.y() << " " << p3.y() << " " << p4.y() << "\n";
//		T x1 = priv::min(p1.x(), p2.x(), p3.x(), p4.x());
//		T y1 = priv::min(p1.y(), p2.y(), p3.y(), p4.y());
//		T x2 = priv::max(p1.x(), p2.x(), p3.x(), p4.x());
//		T y2 = priv::max(p1.y(), p2.y(), p3.y(), p4.y());
//		std::cout << "> minx:" << x1 << " miny:" << y1 << " maxx:" << x2 << " maxy:" << y2 << "\n";
//		return Rect(x1, y1, x2-x1, y2-y1);
		T x = priv::min(a.x(), b.x());
		T y = priv::min(a.y(), b.y());
		return Rect(x,
					y,
					priv::max(a.x() + a.width(), b.x() + b.width()) - x,
					priv::max(a.y() + a.height(), b.y() + b.height()) - y);
	}

	const Vec<T>& position() const {return mPosition; }
	const Vec<T>& size() const {return mSize; }
	T width() const {return mSize.x(); }
	T height() const {return mSize.y(); }
	T x() const {return mPosition.x(); }
	T y() const {return mPosition.y(); }
	T left() const {return mPosition.x(); }
	T right() const {return mPosition.x() + mSize.x(); }
	T top() const {return mPosition.y(); }
	T bottom() const {return mPosition.y() + mSize.y(); }

	Rect& set(const Vec<T>& position, const Vec<T>& size) {
		mPosition = position;
		mSize = size;
		return *this;
	}
	Rect& set(T x, T y, T w, T h) {
		mPosition.set(x, y);
		mSize.set(x, y);
		return *this;
	}

	Rect& setSize(const Vec<T>& size) {
		mSize = size;
		return *this;
	}

	Rect& setPosition(const Vec<T>& position) {
		mPosition = position;
		return *this;
	}

	bool contains(const Vec<T>& p) const {
		return (p >= mPosition) && (p <= (mPosition + mSize));
	}
	bool contains(T x, T y) const {
		return (x >= mPosition.x() &&
				y >= mPosition.y() &&
				(x <= mPosition.x() + mSize.x()) &&
				(y <= mPosition.y() + mSize.y()));
	}

	template <class U>
	Rect<U> to() const {
		return Rect<U>(static_cast<U>(mPosition.x()), static_cast<U>(mPosition.y()),
						static_cast<U>(mSize.x()), static_cast<U>(mSize.y()));
	}
private:
	Vec<T> mPosition;
	Vec<T> mSize;
};

typedef Rect<int> IRect;
typedef Rect<float> FRect;
typedef Rect<double> DRect;

template <class T>
std::ostream& operator<<(std::ostream& out, const Rect<T>& rc) {
	out << "Rect<" << rc.x() << ", " << rc.y() << ", " << rc.width() << ", " << rc.height() << ">";
	return out;
}

//template <class T1, class T2>
//Rect<T1> operator Rect<T2>(const Rect<T2>& obj) {
//	return Rect<T1>(static_cast<T1>(obj.x()), static_cast<T1>(obj.w()),
//					static_cast<T1>(obj.width()), static_cast<T1>(obj.height()));
//}

#endif /* RECT_H_ */
